#include "osip_net.h"
#include "osip_mt.h"
#include "common.h"
#include "log4z.h"

int fdClose(socket_t& fd)
{
	if (SOCKET_ERROR != fd) {
#ifdef WIN32
		closesocket(fd);
#else
		close(fd);
#endif
		fd = -1;
	}
	return -1;
}

int udp_sockopt(socket_t fd)
{
	int optval = 0x01;
#ifdef WIN32
	u_long flag = 0x01;
	ioctlsocket(fd, FIONBIO, &flag);
#else

	int flags = fcntl(fd, F_GETFL, 0);

	if (flags == -1) {
		return -1;
	}

	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
		return -1;
	}
#endif

	optval = 0x400000;
	if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
		(char*)&optval, sizeof(int))) {
		return -1;
	}

	if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
		(char*)&optval, sizeof(int))) {
		return -1;
	}
#if	0
	{
		int  snd_size = 0, rcv_size = 0;
		socklen_t optlen = sizeof(snd_size);

		if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &snd_size, &optlen)) {
			return -1;
		}
		printf("udp send_buf = %d \n", snd_size);

		if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen)) {
			return -1;
		}
		printf("udp recv_buf = %d \n", rcv_size);

	}
#endif
	return 0;
}

int tcp_sockopt(socket_t fd)
{
	int optval = 0x01;
#ifdef WIN32
	u_long flag = 0x01;
	ioctlsocket(fd, FIONBIO, &flag);
#else

	// 设置 SOCK_CLOEXEC 标志
	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
		return -1;
	}

	int flags = fcntl(fd, F_GETFL, 0);

	if (flags == -1) {
		return -1;
	}

	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
		return -1;
	}
#endif

	optval = 0x400000;
	if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
		(char*)&optval, sizeof(int))) {
		return -1;
	}

	if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
		(char*)&optval, sizeof(int))) {
		return -1;
	}

	optval = 0x01;
	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
		(char*)&optval, sizeof(int))) {
		return WSAGetLastError();
	}

	if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
		(const char*)&optval, sizeof(int))) {
		return -1;
	}
#if	0
	{
		int  snd_size = 0, rcv_size = 0;
		socklen_t optlen = sizeof(snd_size);

		if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &snd_size, &optlen)) {
			return -1;
		}
		printf("tcp send_buf = %d \n", snd_size);

		if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen)) {
			return -1;
		}
		printf("tcp recv_buf fd = %d \n", rcv_size);

	}
#endif
	return 0;
}

int listen_sockopt(socket_t fd)
{
	int optval = 0x01;
#ifdef WIN32
	u_long flag = 0x01;
	ioctlsocket(fd, FIONBIO, &flag);
#else
	int flags = fcntl(fd, F_GETFL, 0);
	if (flags == -1) {
		return -1;
	}
	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
		return -1;
	}
#endif

	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
		(char*)&optval, sizeof(int))) {
		return -1;
	}
	return 0;
}

int net_localaddr(socket_t s, struct sockaddr* addr)
{
	socklen_t fdlen = sizeof(struct sockaddr_in);
	if (SOCKET_ERROR == getsockname(s, addr, &fdlen)) {
		return -1;
	}
	return 0;
}

int net_remotaddr(socket_t s, struct sockaddr* addr)
{
	socklen_t fdlen = sizeof(struct sockaddr_in);
	if (SOCKET_ERROR == getpeername(s, addr, &fdlen)) {
		return -1;
	}
	return 0;
}

int ipv4_addr(struct sockaddr_in* addr, const char* ip, unsigned short nPort)
{
	memset(addr, 0, sizeof(struct sockaddr_in));

	addr->sin_family = AF_INET;
	addr->sin_port = htons(nPort);
	addr->sin_addr.s_addr = INADDR_ANY;

	if (NULL == ip || strlen(ip) == 0) {
		return 0;
	}
	return (inet_pton(AF_INET, ip, &addr->sin_addr) > 0) ? 0 : -1;
}

int udp_bind(socket_t* fd, const char* ip, unsigned short nPort)
{
	struct sockaddr_in addr = { 0 };
	*fd = socket(AF_INET, SOCK_DGRAM, 0);

	if (ipv4_addr(&addr, ip, nPort) ||
		(*fd == -1)) {
		return -1;
	}

	if (bind(*fd, (struct sockaddr*)&addr,
		sizeof(struct sockaddr_in))) {
		return -1;
	}

	return udp_sockopt(*fd);
}

int tcp_bind(socket_t* fd, const char* ip, unsigned short nPort)
{
	struct sockaddr_in addr = { 0 };
	*fd = socket(AF_INET, SOCK_STREAM, 0);

	if (ipv4_addr(&addr, ip, nPort) ||
		(*fd == -1)) {
		return -1;
	}

	if (bind(*fd, (struct sockaddr*)&addr,
		sizeof(struct sockaddr_in))) {
		return -1;
	}

	return tcp_sockopt(*fd);
}

#ifdef _WIN32
int tcp_listen(SOCKET* fd, const char* ip, unsigned short nPort)
{
	struct sockaddr_in addr = { 0 };
	WSADATA wsaData;
	int result;

	// Initialize Winsock
	result = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (result != 0) {
		return -1;
	}

	*fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (*fd == INVALID_SOCKET) {
		WSACleanup();
		return -1;
	}

	addr.sin_family = AF_INET;
	addr.sin_port = htons(nPort);
	if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) {
		closesocket(*fd);
		WSACleanup();
		return -1;
	}

	if (bind(*fd, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
		closesocket(*fd);
		WSACleanup();
		return -1;
	}

	if (listen(*fd, SOMAXCONN) == SOCKET_ERROR) {
		closesocket(*fd);
		WSACleanup();
		return -1;
	}

	return 0;
}
#else
int tcp_listen(socket_t* fd, const char* ip, unsigned short nPort)
{
	struct sockaddr_in addr = { 0 };
	if ((*fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) <= 0) {
		return -1;
	}

	if (listen_sockopt(*fd) || ipv4_addr(&addr, ip, nPort)) {
		return -1;
	}

	if (bind(*fd, (struct sockaddr*)&addr, sizeof(addr))) {
		return -1;
	}

	return listen(*fd, 0x400);
}
#endif

#ifdef _WIN32
int initListenFd(unsigned short port)
{
	WSADATA wsaData;
	SOCKET lfd;
	struct sockaddr_in addr;
	int ret;

	// Initialize Winsock
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
		return -1;
	}

	// Create socket
	lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (lfd == INVALID_SOCKET) {
		WSACleanup();
		return -1;
	}

	// Set socket options
	// int opt = 1;
	// ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
	// if (ret == SOCKET_ERROR) {
	//     closesocket(lfd);
	//     WSACleanup();
	//     return -1;
	// }

	if (listen_sockopt(lfd) != 0) {
		closesocket(lfd);
		WSACleanup();
		return -1;
	}

	// Bind socket
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = INADDR_ANY;
	ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
	if (ret == SOCKET_ERROR) {
		closesocket(lfd);
		WSACleanup();
		return -1;
	}

	// Listen for connections
	ret = listen(lfd, SOMAXCONN);
	if (ret == SOCKET_ERROR) {
		closesocket(lfd);
		WSACleanup();
		return -1;
	}

	return lfd;
}
#else
int initListenFd(unsigned short port)
{
	// 1. 创建监听的fd
	int lfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); //第一个参数指定ipv4，第二个参数指定tcp
	if (lfd == -1)
	{
		perror("socket");
		LOGFMTE("socket error message: %s\n", strerror(errno));
		return -1;
	}

	// 2. 设置端口复用
	int opt = 1;
	int ret = 0;
	// ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
	// if (ret == -1)
	// {
	//     perror("setsockopt");
	//     return -1;
	// }

	if (listen_sockopt(lfd)) {
		return -1;
	}

	// 3. 绑定端口
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = INADDR_ANY;
	ret = bind(lfd, (struct sockaddr*)&addr, sizeof addr);
	if (ret == -1)
	{
		perror("bind");
		LOGFMTE("bind error message: %s", strerror(errno));
		return -1;
	}

	// 4. 设置监听
	ret = listen(lfd, 128); //一次最多接受多少个客户端连接，这次不行下次还可以。内核中最大为128。
	if (ret == -1)
	{
		perror("listen");
		LOGFMTE("listen error message: %s", strerror(errno));
		return -1;
	}

	// 返回监听的fd
	return lfd;
}
#endif

#ifdef _WIN32
int tcp_client(SOCKET* fd, const char* ip, unsigned short nPort)
{
	WSADATA wsaData;
	int result;
	struct sockaddr_in addr;

	// Initialize Winsock
	result = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (result != 0) {
		return -1;
	}

	// Create socket
	*fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (*fd == INVALID_SOCKET) {
		WSACleanup();
		return -1;
	}

	// Set socket options
	// int opt = 1;
	// result = setsockopt(*fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
	// if (result == SOCKET_ERROR) {
	//     closesocket(*fd);
	//     WSACleanup();
	//     return -1;
	// }

	if (tcp_connect(*fd, ip, nPort) != 0) {
		closesocket(*fd);
		WSACleanup();
		return -1;
	}

	return 0;
}
#else
int tcp_client(socket_t* fd, const char* ip, unsigned short nPort)
{
	struct sockaddr_in addr = { 0 };
	if ((*fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) <= 0) {
		return -1;
	}
	if (tcp_connect(*fd, ip, nPort)) {
		return -1;
	}
	return 0;
}
#endif

int tcp_connect(socket_t fd, const char* ip, unsigned short nPort, int tm)
{
	int nRet = -1;
	struct sockaddr_in addr;
	ipv4_addr(&addr, ip, nPort);
	nRet = connect(fd, (struct sockaddr*)&addr, sizeof(addr));

#ifdef WIN32
	if ((-1 == nRet) && (WSAGetLastError() == WSAEWOULDBLOCK)) {

		fd_set fdset;
		FD_ZERO(&fdset);
		FD_SET(fd, &fdset);

		struct timeval tv = { tm,0 };
		int optval = 0, len = sizeof(int);
		if (select(fd, NULL, &fdset, NULL, &tv) > 0) {
			getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&optval, &len);
			if (optval == 0) {
				return 0;
			}
		}
	}
#else
	if ((-1 == nRet) && (errno == EINPROGRESS)) {
		struct tcp_info info;
		socklen_t len = sizeof(struct tcp_info);
		unsigned int dwTick = TimGetTicks() + tm * 1000;
		do {
			if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t*)&len) == 0) {
				if (info.tcpi_state == TCP_ESTABLISHED) {
					return 0;
				}
			}
			usleep(200);
		} while (TimGetTicks() < dwTick);
	}
#endif
	return nRet;
}

int tcp_sendmessage(socket_t fd, const char* data, int len)
{
	int dwByte, dwWrite = 0;

	do {
#ifdef WIN32
		dwByte = send(fd, data + dwWrite, len, 0);
		if (dwByte < 0) {
			if (WSAEWOULDBLOCK == WSAGetLastError()) {
				continue;
			}
			return -1;
		}
#else
		dwByte = send(fd, data + dwWrite, len, MSG_NOSIGNAL);
		if (dwByte < 0) {
			if (EAGAIN == errno || errno == EWOULDBLOCK) {
				continue;
			}
			return -1;
		}
#endif
		len -= dwByte;
		dwWrite += dwByte;
	} while (len > 0);

	return 0;
}

int udp_sendmessage(socket_t fd, const char* data, int len, sockaddr_in* addr)
{
	if (len != sendto(fd, data, len, 0,
		(struct sockaddr*)addr, sizeof(struct sockaddr_in))) {
		printf("udp message faile\n");
		return -1;
	}
	return 0;
}

int thread_name(socket_t fd, const char* name)
{
#if	0
	int  snd_size = 0, rcv_size = 0;
	socklen_t optlen = sizeof(snd_size);

	if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &snd_size, &optlen)) {
		return -1;
	}
	printf("%s send_buf = %d \n", name, snd_size);

	if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen)) {
		return -1;
	}
	printf("%s recv_buf = %d \n", name, rcv_size);
#endif

#ifndef WIN32
	prctl(PR_SET_NAME, name);
#endif

	return 0;
}


